# import third-party modules
from enum import Enum
from PIL import Image
import tkinter as tk
import os
import cv2
import numpy as np

# import local modules
from supports import ScrollbarWithEntry,ImageCanvas,GetFileExtension
from app_base import App_Base
from log import *

class Show_Image_Type(Enum):
    ORIGINAL = 0
    HVS_RANGE = 1
    HVS = 2

class File_Fetcher(tk.LabelFrame):
    '''文件读取器'''
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)
        self.file_path = tk.StringVar()
        self.file_path.set('D:/PolarBear/#other/能量机关（黑暗环境）/关灯-红方小能量机关-全激活过程.MP4')
        # self.file_path.set('D:/PolarBear/#Software_Development/HSV_Preview/test/test_block.png')
        self.file_path.trace("w", self.LoadFile)
        self.cap = None
        self.is_reading_video = False

    def AddWidget(self)->None:
        self.label = tk.Label(self, text='文件路径')
        self.label.pack(side=tk.LEFT)
        self.entry = tk.Entry(self, textvariable=self.file_path, width=50)
        self.entry.configure(highlightcolor="red")  # 设置Entry的边框颜色为红色
        self.entry.pack(side=tk.LEFT, fill=tk.X, expand=1)

    def LoadFile(self, *args)->None:
        if not self.FileExists():
            self.entry.configure(highlightcolor="red", highlightthickness=2)  # 设置Entry的边框厚度
            if self.is_reading_video:
                self.is_reading_video = False
                self.cap.release()
            return
        self.entry.configure(highlightthickness=0)  # 设置Entry无高亮边框
        file_extension = GetFileExtension(self.file_path.get())
        LogDebug('File extension: ' + file_extension)
        if file_extension.lower() == '.mp4':
            self.LoadVideo()
        elif file_extension.lower() in ['.jpg', '.png', '.bmp']:
            self.LoadImage()

    def LoadVideo(self):
        '''读取视频'''
        if not self.is_reading_video:
            self.cap = cv2.VideoCapture(self.file_path.get())
            self.is_reading_video = True
        
        ret, frame = self.cap.read()
        if not ret:
            self.is_reading_video = False

        if not self.is_reading_video:
            self.cap.release()
            return None
        width = frame.shape[1]
        height = frame.shape[0]
        self.CallUpdateImage(frame, width, height)
        self.after(16, self.LoadVideo)
        return frame

    def LoadImage(self):
        '''读取图片'''
        img = cv2.imread(self.file_path.get())
        width = img.shape[1]
        height = img.shape[0]
        self.CallUpdateImage(img, width, height)
        return img

    def FileExists(self)->bool:
        '''检查文件是否存在'''
        return os.path.exists(self.file_path.get())

    def ShowError(self):
        self.showpopup = not self.showpopup
        if self.showpopup:
            self.popup = tk.Toplevel()
            self.popup.wm_overrideredirect(True)  # 去掉窗口边框
            label = tk.Label(self.popup, text="这是一个提示", bg="white", relief="solid", borderwidth=1)
            label.pack()
            # 获取self.hi_there的屏幕坐标和尺寸
            x = self.entry.winfo_rootx()
            y = self.entry.winfo_rooty()
            width = self.entry.winfo_width()
            height = self.entry.winfo_height()

            # 计算self.popup的位置
            popup_height = 20  # 你可以根据实际情况调整这个值
            popup_y = y - popup_height
            self.popup.wm_geometry(f"+{x+width//2}+{popup_y}")
        else:
            self.popup.destroy()

    def CallUpdateImage(self, img_:np.ndarray, width:int, height:int)->None:
        '''调用图像更新回调函数'''
        return

class Range_Selector(tk.LabelFrame):
    '''获取HSV的高低范围'''
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)

    def AddWidget(self)->None:
        '''增添组件'''
        labelframe_low_range = tk.LabelFrame(self, text='低范围')
        labelframe_low_range.pack(side=tk.LEFT, fill=tk.X, expand=1)
        self.selector_low_H = ScrollbarWithEntry(labelframe_low_range)
        self.selector_low_H.pack(side=tk.TOP)
        self.selector_low_H.ConfigLabel(text='H')
        self.selector_low_H.ConfigScale(from_=0, to=180)
        self.selector_low_S = ScrollbarWithEntry(labelframe_low_range)
        self.selector_low_S.pack(side=tk.TOP)
        self.selector_low_S.ConfigLabel(text='S')
        self.selector_low_S.ConfigScale(from_=0, to=255)
        self.selector_low_V = ScrollbarWithEntry(labelframe_low_range)
        self.selector_low_V.pack(side=tk.TOP)
        self.selector_low_V.ConfigLabel(text='V')
        self.selector_low_V.ConfigScale(from_=0, to=255)
        
        labelframe_high_range = tk.LabelFrame(self, text='高范围')
        labelframe_high_range.pack(side=tk.LEFT, fill=tk.X, expand=1)
        self.selector_high_H = ScrollbarWithEntry(labelframe_high_range)
        self.selector_high_H.pack(side=tk.TOP)
        self.selector_high_H.ConfigLabel(text='H')
        self.selector_high_H.ConfigScale(from_=0, to=180)
        self.selector_high_S = ScrollbarWithEntry(labelframe_high_range)
        self.selector_high_S.pack(side=tk.TOP)
        self.selector_high_S.ConfigLabel(text='S')
        self.selector_high_S.ConfigScale(from_=0, to=255)
        self.selector_high_V = ScrollbarWithEntry(labelframe_high_range)
        self.selector_high_V.pack(side=tk.TOP)
        self.selector_high_V.ConfigLabel(text='V')
        self.selector_high_V.ConfigScale(from_=0, to=255)
        LogInfo('Range Selector widgets added.')

    def PackInnerWidgets(self):
        '''
        将内部的组件打包
        '''
        self.selector_low_H.PackInnerWidget()
        self.selector_low_S.PackInnerWidget()
        self.selector_low_V.PackInnerWidget()
        self.selector_high_H.PackInnerWidget()
        self.selector_high_S.PackInnerWidget()
        self.selector_high_V.PackInnerWidget()
        LogInfo('Range Selector inner widgets packed.')

    def BindSelectorValueChange(self):
        self.selector_low_H.value.trace("w", self.CallUpdateShowImage)
        self.selector_low_S.value.trace("w", self.CallUpdateShowImage)
        self.selector_low_V.value.trace("w", self.CallUpdateShowImage)
        self.selector_high_H.value.trace("w", self.CallUpdateShowImage)
        self.selector_high_S.value.trace("w", self.CallUpdateShowImage)
        self.selector_high_V.value.trace("w", self.CallUpdateShowImage)

    def GetRange(self):
        '''
        获取HSV的高低范围
        return: tuple(tuple(low_H, low_S, low_V), tuple(high_H, high_S, high_V))
        '''
        low_H = self.selector_low_H.value.get()
        low_S = self.selector_low_S.value.get()
        low_V = self.selector_low_V.value.get()

        high_H = self.selector_high_H.value.get()
        high_S = self.selector_high_S.value.get()
        high_V = self.selector_high_V.value.get()

        low_range = (low_H, low_S, low_V)
        high_range = (high_H, high_S, high_V)
        range_ = (low_range, high_range)
        return range_

    def GetRangeCallback(self):
        return self.GetRange()

    def CallUpdateShowImage(self, *args)->None:
        '''调用图像更新回调函数'''
        return

class Graph_Previewer(tk.LabelFrame):
    '''图像预览器'''
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)
        self.show_image_type = Show_Image_Type.ORIGINAL
        self.original_image = np.zeros((375, 500, 3), dtype=np.uint8)
        self.show_image = Image.fromarray(self.original_image)

    def AddWidget(self):
        self.canvas = ImageCanvas(self, width=500, height=375)
        self.canvas.pack(side=tk.TOP)
        self.canvas.UpdateImage(image=self.show_image)
        LogInfo('Graph Previewer widgets added.')

    def UpdateShowImage(self):
        if self.show_image_type == Show_Image_Type.ORIGINAL:
            img_rgb = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2RGB)# 首先，将BGR图像转换为RGB图像，因为PIL使用RGB格式
            self.show_image = Image.fromarray(img_rgb)# 然后，将NumPy数组转换为PIL图像
        elif self.show_image_type == Show_Image_Type.HVS_RANGE:
            hsv_img = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2HSV)
            range_ = self.CallGetRange()
            lower_range = range_[0]
            upper_range = range_[1]
            mask = cv2.inRange(hsv_img, lower_range, upper_range)
            self.show_image = Image.fromarray(mask)
        elif self.show_image_type == Show_Image_Type.HVS:
            hsv_img = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2HSV)
            self.show_image = Image.fromarray(cv2.cvtColor(hsv_img, cv2.COLOR_BGR2RGB))
        self.canvas.UpdateImage(image=self.show_image)

    def CallGetRange(self):
        pass

    def SetTypeCallback(self, type_:Show_Image_Type):
        self.show_image_type = type_
        self.UpdateShowImage()
        LogDebug('Graph Previewer type set to ' + str(self.show_image_type))

    def UpdateImageCallback(self, img:np.ndarray, width:int, height:int)->None:
        self.original_image = img.copy()
        self.UpdateShowImage()

    def UpadateShowImageCallback(self, *args):
        self.UpdateShowImage()

class Menubar(tk.Menu):
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)
        self.show_image_type = Show_Image_Type.ORIGINAL

    def AddMenu(self):
        # 创建一个名为"图片显示类型"的下拉菜单，并将其添加到菜单栏中
        show_menu = tk.Menu(self, tearoff=0)
        self.add_cascade(label="图片显示类型", menu=show_menu)

        show_menu.add_command(label="原图", command=lambda:self.CallSetType(Show_Image_Type.ORIGINAL))# 在"图片显示类型"菜单中添加一个名为"原图"的选项
        show_menu.add_command(label="HVS选择范围", command=lambda:self.CallSetType(Show_Image_Type.HVS_RANGE))
        show_menu.add_command(label="HVS可视化", command=lambda:self.CallSetType(Show_Image_Type.HVS))
    
    def CallSetType(self, type_:Show_Image_Type):
        pass

class HSV_Previewer(App_Base):
    def __init__(self, app_name='app_name', version='V1.1.1', resizable=True, width=500, height=650):
        super().__init__(app_name, version, resizable, width, height)

    def AddWidget(self):
        self.menubar = Menubar(self.root)
        self.menubar.AddMenu()
        self.root.config(menu=self.menubar)
        
        frame_range_and_preview = tk.Frame(self.root)
        frame_range_and_preview.pack(side=tk.TOP, fill=tk.X, expand=1)
        
        self.file_fetcher = File_Fetcher(frame_range_and_preview, text='文件读取器')
        self.file_fetcher.pack(side=tk.TOP, fill=tk.X, expand=1)
        self.file_fetcher.AddWidget()
        
        self.graph_previewer = Graph_Previewer(frame_range_and_preview, text='图像预览')
        self.graph_previewer.pack(side=tk.TOP, fill=tk.X, expand=1)
        self.graph_previewer.AddWidget()
        
        self.range_selector = Range_Selector(frame_range_and_preview, text='选择HSV范围')
        self.range_selector.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        self.range_selector.AddWidget()
        self.range_selector.PackInnerWidgets()
        
        #建立回调函数连接
        self.file_fetcher.CallUpdateImage = self.graph_previewer.UpdateImageCallback
        self.menubar.CallSetType = self.graph_previewer.SetTypeCallback
        self.graph_previewer.CallGetRange = self.range_selector.GetRangeCallback
        self.range_selector.CallUpdateShowImage = self.graph_previewer.UpadateShowImageCallback
        
        #建立变量关联
        pass
        
        self.range_selector.BindSelectorValueChange()

if __name__ == '__main__':
    app = HSV_Previewer(
        app_name='HSV Previewer',
        resizable=False,
    )
    LogInfo('App version: ' + app.version)
    app.CreateApp()
    LogInfo('App created.')
    app.AddWidget()
    app.RunApp()